package com.atguigu.study.bootredis.zookeeper.lock;

import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

/**
 * @author jiangta
 * @version 1.0
 * @date 2021/1/7 3:47 下午
 */
@Slf4j
public class ZkLock implements AutoCloseable, Watcher {

    private ZooKeeper zooKeeper;
    private String znode;

    public ZkLock() {
        try {
            this.zooKeeper = new ZooKeeper("10.19.205.97:2181", 10000, this);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public boolean getLock(String businessCode) {
        try {
            //创建业务 根节点
            Stat stat = zooKeeper.exists("/" + businessCode, false);
            if (stat == null) {
                zooKeeper.create("/" + businessCode, businessCode.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
                        CreateMode.PERSISTENT);
            }

            //创建瞬时有序节点
            znode = zooKeeper.create("/" + businessCode + "/" + businessCode + "_", businessCode.getBytes(),
                    ZooDefs.Ids.OPEN_ACL_UNSAFE,
                    CreateMode.EPHEMERAL_SEQUENTIAL);
            //获取业务 根节点下所有的子节点
            List<String> zooKeeperChildren = zooKeeper.getChildren("/" + businessCode, false);
            Collections.sort(zooKeeperChildren);
            String firstNode = zooKeeperChildren.get(0);
            //如果创建的节点是第一个节点，则获得锁
            if (znode.endsWith(firstNode)) {
                return true;
            }

            //不是第一个节点，则监听前一个节点
            String lastNode = firstNode;
            for (String node : zooKeeperChildren) {
                if (znode.endsWith(node)) {
                    zooKeeper.exists("/" + businessCode + "/" + lastNode, true);
                    break;
                } else {
                    lastNode = node;
                }
            }

            synchronized (this) {
                wait();
            }
            return true;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    @Override
    public void close() throws Exception {
        zooKeeper.delete(znode,-1);
        zooKeeper.close();
        log.info("zookeeper 释放");
    }

    @Override
    public void process(WatchedEvent watchedEvent) {

        if(watchedEvent.getType() == Event.EventType.NodeDeleted) {
            synchronized (this) {
                notify();
            }
        }
    }
}
